use std::sync::Mutex;

/// ## Blocking Queue
///   can blocking and wite to pop \
///   and locking by mutex when many thread want to visist
/// #### HOW TO USE
/// ```rust
/// use lazy_static::lazy_static;
/// use std::time::Duration;
/// use std::time::Instant;
///
/// lazy_static! {
///         static ref BQ : BlockingQueue<String> = BlockingQueue::<String>::default();
/// }
///
/// #[test]
/// fn test() {
///     let ins = Instant::now();
///     std::thread::spawn(|| {
///         std::thread::sleep(Duration::new(3, 0));
///         BQ.push("hi2".to_string());
///     });
///     std::thread::spawn(|| {
///         std::thread::sleep(Duration::new(1, 0));
///         BQ.push("hi1".to_string());
///     });
///     let s = BQ.pop();
///     println!("s1: {} ins: {} ms", s, ins.elapsed().as_millis());
///     let s2 = BQ.pop();
///     println!("s2: {} ins: {} ms", s2, ins.elapsed().as_millis());
/// }
/// ```
///
#[derive(Default)]
pub struct BlockingQueue<T> {
    vec: Mutex<Vec<T>>,
}

impl<T> BlockingQueue<T> {
    pub fn push(&self, t: T) {
        self.vec.lock().unwrap().push(t);
    }

    pub fn pop(&self) -> T {
        loop {
            let mut v = self.vec.lock().unwrap();
            if !v.is_empty() {
                return v.pop().unwrap();
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use lazy_static::lazy_static;
    use std::time::Duration;
    use std::time::Instant;
    use crate::basic::structure::blocking::BlockingQueue;

    lazy_static! {
        static ref BQ : BlockingQueue<String> = BlockingQueue::<String>::default();
    }

    #[test]
    fn test() {
        let ins = Instant::now();
        std::thread::spawn(|| {
            std::thread::sleep(Duration::new(3, 0));
            BQ.push("hi2".to_string());
        });
        std::thread::spawn(|| {
            std::thread::sleep(Duration::new(1, 0));
            BQ.push("hi1".to_string());
        });
        let s = BQ.pop();
        println!("s1: {} ins: {} ms", s, ins.elapsed().as_millis());
        let s2 = BQ.pop();
        println!("s2: {} ins: {} ms", s2, ins.elapsed().as_millis());
    }
}